home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / lib / pymodules / python2.6 / pyinotify.pyc (.txt) < prev   
Encoding:
Python Compiled Bytecode  |  2009-10-28  |  60.6 KB  |  1,724 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. '''
  5. pyinotify
  6.  
  7. @author: Sebastien Martini
  8. @license: GPLv2+
  9. @contact: seb@dbzteam.org
  10. '''
  11.  
  12. class PyinotifyError(Exception):
  13.     '''Indicates exceptions raised by a Pyinotify class.'''
  14.     pass
  15.  
  16.  
  17. class UnsupportedPythonVersionError(PyinotifyError):
  18.     '''
  19.     Raised for unsupported Python version.
  20.     '''
  21.     
  22.     def __init__(self, version):
  23.         '''
  24.         @param version: Current Python version
  25.         @type version: string
  26.         '''
  27.         PyinotifyError.__init__(self, 'Python %s is unsupported, requires at least Python 2.4' % version)
  28.  
  29.  
  30.  
  31. class UnsupportedLibcVersionError(PyinotifyError):
  32.     '''
  33.     Raised for unsupported libc version.
  34.     '''
  35.     
  36.     def __init__(self, version):
  37.         '''
  38.         @param version: Current Libc version
  39.         @type version: string
  40.         '''
  41.         PyinotifyError.__init__(self, 'Libc %s is unsupported, requires at least Libc 2.4' % version)
  42.  
  43.  
  44. import sys
  45. if sys.version < '2.4':
  46.     raise UnsupportedPythonVersionError(sys.version)
  47. sys.version < '2.4'
  48. import threading
  49. import os
  50. import select
  51. import struct
  52. import fcntl
  53. import errno
  54. import termios
  55. import array
  56. import logging
  57. import atexit
  58. from collections import deque
  59. from datetime import datetime, timedelta
  60. import time
  61. import fnmatch
  62. import re
  63. import ctypes
  64. import ctypes.util as ctypes
  65. __author__ = 'seb@dbzteam.org (Sebastien Martini)'
  66. __version__ = '0.8.6'
  67. __metaclass__ = type
  68. LIBC = ctypes.cdll.LoadLibrary(ctypes.util.find_library('c'))
  69. LIBC.gnu_get_libc_version.restype = ctypes.c_char_p
  70. LIBC_VERSION = LIBC.gnu_get_libc_version()
  71. if int(LIBC_VERSION.split('.')[0]) < 2 and int(LIBC_VERSION.split('.')[0]) == 2 and int(LIBC_VERSION.split('.')[1]) < 4:
  72.     raise UnsupportedLibcVersionError(LIBC_VERSION)
  73. int(LIBC_VERSION.split('.')[1]) < 4
  74. log = logging.getLogger('pyinotify')
  75. console_handler = logging.StreamHandler()
  76. console_handler.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
  77. log.addHandler(console_handler)
  78. log.setLevel(20)
  79.  
  80. try:
  81.     if False:
  82.         import psyco
  83.         psyco.full()
  84. except ImportError:
  85.     pass
  86.  
  87.  
  88. class SysCtlINotify:
  89.     """
  90.     Access (read, write) inotify's variables through sysctl.
  91.  
  92.     Examples:
  93.       - Read variable: myvar = max_queued_events.value
  94.       - Update variable: max_queued_events.value = 42
  95.     """
  96.     inotify_attrs = {
  97.         'max_user_instances': 1,
  98.         'max_user_watches': 2,
  99.         'max_queued_events': 3 }
  100.     
  101.     def __init__(self, attrname):
  102.         sino = ctypes.c_int * 3
  103.         self._attrname = attrname
  104.         self._attr = sino(5, 20, SysCtlINotify.inotify_attrs[attrname])
  105.  
  106.     
  107.     def get_val(self):
  108.         '''
  109.         @return: stored value.
  110.         @rtype: int
  111.         '''
  112.         oldv = ctypes.c_int(0)
  113.         size = ctypes.c_int(ctypes.sizeof(oldv))
  114.         LIBC.sysctl(self._attr, 3, ctypes.c_voidp(ctypes.addressof(oldv)), ctypes.addressof(size), None, 0)
  115.         return oldv.value
  116.  
  117.     
  118.     def set_val(self, nval):
  119.         '''
  120.         @param nval: set to nval.
  121.         @type nval: int
  122.         '''
  123.         oldv = ctypes.c_int(0)
  124.         sizeo = ctypes.c_int(ctypes.sizeof(oldv))
  125.         newv = ctypes.c_int(nval)
  126.         sizen = ctypes.c_int(ctypes.sizeof(newv))
  127.         LIBC.sysctl(self._attr, 3, ctypes.c_voidp(ctypes.addressof(oldv)), ctypes.addressof(sizeo), ctypes.c_voidp(ctypes.addressof(newv)), ctypes.addressof(sizen))
  128.  
  129.     value = property(get_val, set_val)
  130.     
  131.     def __repr__(self):
  132.         return '<%s=%d>' % (self._attrname, self.get_val())
  133.  
  134.  
  135. for attrname in ('max_queued_events', 'max_user_instances', 'max_user_watches'):
  136.     globals()[attrname] = SysCtlINotify(attrname)
  137.  
  138.  
  139. def iglob(pathname):
  140.     if not has_magic(pathname):
  141.         if hasattr(os.path, 'lexists'):
  142.             if os.path.lexists(pathname):
  143.                 yield pathname
  144.             
  145.         elif os.path.islink(pathname) or os.path.exists(pathname):
  146.             yield pathname
  147.         
  148.         return None
  149.     (dirname, basename) = os.path.split(pathname)
  150.     if not dirname:
  151.         return None
  152.     if has_magic(basename):
  153.         glob_in_dir = glob1
  154.     else:
  155.         glob_in_dir = glob0
  156.     for dirname in dirs:
  157.         for name in glob_in_dir(dirname, basename):
  158.             yield os.path.join(dirname, name)
  159.         
  160.     
  161.  
  162.  
  163. def glob1(dirname, pattern):
  164.     if not dirname:
  165.         dirname = os.curdir
  166.     
  167.     
  168.     try:
  169.         names = os.listdir(dirname)
  170.     except os.error:
  171.         return []
  172.  
  173.     return fnmatch.filter(names, pattern)
  174.  
  175.  
  176. def glob0(dirname, basename):
  177.     if basename == '' and os.path.isdir(dirname):
  178.         return [
  179.             basename]
  180.     if hasattr(os.path, 'lexists'):
  181.         if os.path.lexists(os.path.join(dirname, basename)):
  182.             return [
  183.                 basename]
  184.     elif os.path.islink(os.path.join(dirname, basename)) or os.path.exists(os.path.join(dirname, basename)):
  185.         return [
  186.             basename]
  187.     os.path.isdir(dirname)
  188.     return []
  189.  
  190. magic_check = re.compile('[*?[]')
  191.  
  192. def has_magic(s):
  193.     return magic_check.search(s) is not None
  194.  
  195.  
  196. class EventsCodes:
  197.     """
  198.     Set of codes corresponding to each kind of events.
  199.     Some of these flags are used to communicate with inotify, whereas
  200.     the others are sent to userspace by inotify notifying some events.
  201.  
  202.     @cvar IN_ACCESS: File was accessed.
  203.     @type IN_ACCESS: int
  204.     @cvar IN_MODIFY: File was modified.
  205.     @type IN_MODIFY: int
  206.     @cvar IN_ATTRIB: Metadata changed.
  207.     @type IN_ATTRIB: int
  208.     @cvar IN_CLOSE_WRITE: Writtable file was closed.
  209.     @type IN_CLOSE_WRITE: int
  210.     @cvar IN_CLOSE_NOWRITE: Unwrittable file closed.
  211.     @type IN_CLOSE_NOWRITE: int
  212.     @cvar IN_OPEN: File was opened.
  213.     @type IN_OPEN: int
  214.     @cvar IN_MOVED_FROM: File was moved from X.
  215.     @type IN_MOVED_FROM: int
  216.     @cvar IN_MOVED_TO: File was moved to Y.
  217.     @type IN_MOVED_TO: int
  218.     @cvar IN_CREATE: Subfile was created.
  219.     @type IN_CREATE: int
  220.     @cvar IN_DELETE: Subfile was deleted.
  221.     @type IN_DELETE: int
  222.     @cvar IN_DELETE_SELF: Self (watched item itself) was deleted.
  223.     @type IN_DELETE_SELF: int
  224.     @cvar IN_MOVE_SELF: Self (watched item itself) was moved.
  225.     @type IN_MOVE_SELF: int
  226.     @cvar IN_UNMOUNT: Backing fs was unmounted.
  227.     @type IN_UNMOUNT: int
  228.     @cvar IN_Q_OVERFLOW: Event queued overflowed.
  229.     @type IN_Q_OVERFLOW: int
  230.     @cvar IN_IGNORED: File was ignored.
  231.     @type IN_IGNORED: int
  232.     @cvar IN_ONLYDIR: only watch the path if it is a directory (new
  233.                       in kernel 2.6.15).
  234.     @type IN_ONLYDIR: int
  235.     @cvar IN_DONT_FOLLOW: don't follow a symlink (new in kernel 2.6.15).
  236.                           IN_ONLYDIR we can make sure that we don't watch
  237.                           the target of symlinks.
  238.     @type IN_DONT_FOLLOW: int
  239.     @cvar IN_MASK_ADD: add to the mask of an already existing watch (new
  240.                        in kernel 2.6.14).
  241.     @type IN_MASK_ADD: int
  242.     @cvar IN_ISDIR: Event occurred against dir.
  243.     @type IN_ISDIR: int
  244.     @cvar IN_ONESHOT: Only send event once.
  245.     @type IN_ONESHOT: int
  246.     @cvar ALL_EVENTS: Alias for considering all of the events.
  247.     @type ALL_EVENTS: int
  248.     """
  249.     FLAG_COLLECTIONS = {
  250.         'OP_FLAGS': {
  251.             'IN_ACCESS': 1,
  252.             'IN_MODIFY': 2,
  253.             'IN_ATTRIB': 4,
  254.             'IN_CLOSE_WRITE': 8,
  255.             'IN_CLOSE_NOWRITE': 16,
  256.             'IN_OPEN': 32,
  257.             'IN_MOVED_FROM': 64,
  258.             'IN_MOVED_TO': 128,
  259.             'IN_CREATE': 256,
  260.             'IN_DELETE': 512,
  261.             'IN_DELETE_SELF': 1024,
  262.             'IN_MOVE_SELF': 2048 },
  263.         'EVENT_FLAGS': {
  264.             'IN_UNMOUNT': 8192,
  265.             'IN_Q_OVERFLOW': 16384,
  266.             'IN_IGNORED': 32768 },
  267.         'SPECIAL_FLAGS': {
  268.             'IN_ONLYDIR': 16777216,
  269.             'IN_DONT_FOLLOW': 33554432,
  270.             'IN_MASK_ADD': 536870912,
  271.             'IN_ISDIR': 1073741824,
  272.             'IN_ONESHOT': 0x80000000L } }
  273.     
  274.     def maskname(mask):
  275.         '''
  276.         Return the event name associated to mask. IN_ISDIR is appended when
  277.         appropriate. Note: only one event is returned, because only one is
  278.         raised once at a time.
  279.  
  280.         @param mask: mask.
  281.         @type mask: int
  282.         @return: event name.
  283.         @rtype: str
  284.         '''
  285.         ms = mask
  286.         name = '%s'
  287.         if mask & IN_ISDIR:
  288.             ms = mask - IN_ISDIR
  289.             name = '%s|IN_ISDIR'
  290.         
  291.         return name % EventsCodes.ALL_VALUES[ms]
  292.  
  293.     maskname = staticmethod(maskname)
  294.  
  295. EventsCodes.ALL_FLAGS = { }
  296. EventsCodes.ALL_VALUES = { }
  297. for flagc, valc in EventsCodes.FLAG_COLLECTIONS.iteritems():
  298.     setattr(EventsCodes, flagc, valc)
  299.     EventsCodes.ALL_FLAGS.update(valc)
  300.     for name, val in valc.iteritems():
  301.         globals()[name] = val
  302.         EventsCodes.ALL_VALUES[val] = name
  303.     
  304.  
  305. ALL_EVENTS = reduce((lambda x, y: x | y), EventsCodes.OP_FLAGS.itervalues())
  306. EventsCodes.ALL_FLAGS['ALL_EVENTS'] = ALL_EVENTS
  307. EventsCodes.ALL_VALUES[ALL_EVENTS] = 'ALL_EVENTS'
  308.  
  309. class _Event:
  310.     '''
  311.     Event structure, represent events raised by the system. This
  312.     is the base class and should be subclassed.
  313.  
  314.     '''
  315.     
  316.     def __init__(self, dict_):
  317.         '''
  318.         Attach attributes (contained in dict_) to self.
  319.         '''
  320.         for tpl in dict_.iteritems():
  321.             setattr(self, *tpl)
  322.         
  323.  
  324.     
  325.     def __repr__(self):
  326.         '''
  327.         @return: String representation.
  328.         @rtype: str
  329.         '''
  330.         s = ''
  331.         for attr, value in sorted(self.__dict__.items(), key = (lambda x: x[0])):
  332.             if attr.startswith('_'):
  333.                 continue
  334.             
  335.             if attr == 'mask':
  336.                 value = hex(getattr(self, attr))
  337.             elif isinstance(value, str) and not value:
  338.                 value = "''"
  339.             
  340.             s += ' %s%s%s' % (Color.FieldName(attr), Color.Punctuation('='), Color.FieldValue(value))
  341.         
  342.         s = '%s%s%s %s' % (Color.Punctuation('<'), Color.ClassName(self.__class__.__name__), s, Color.Punctuation('>'))
  343.         return s
  344.  
  345.  
  346.  
  347. class _RawEvent(_Event):
  348.     """
  349.     Raw event, it contains only the informations provided by the system.
  350.     It doesn't infer anything.
  351.     """
  352.     
  353.     def __init__(self, wd, mask, cookie, name):
  354.         '''
  355.         @param wd: Watch Descriptor.
  356.         @type wd: int
  357.         @param mask: Bitmask of events.
  358.         @type mask: int
  359.         @param cookie: Cookie.
  360.         @type cookie: int
  361.         @param name: Basename of the file or directory against which the
  362.                      event was raised, in case where the watched directory
  363.                      is the parent directory. None if the event was raised
  364.                      on the watched item itself.
  365.         @type name: string or None
  366.         '''
  367.         super(_RawEvent, self).__init__({
  368.             'wd': wd,
  369.             'mask': mask,
  370.             'cookie': cookie,
  371.             'name': name.rstrip('\x00') })
  372.         log.debug(repr(self))
  373.  
  374.  
  375.  
  376. class Event(_Event):
  377.     """
  378.     This class contains all the useful informations about the observed
  379.     event. However, the incorporation of each field is not guaranteed and
  380.     depends on the type of event. In effect, some fields are irrelevant
  381.     for some kind of event (for example 'cookie' is meaningless for
  382.     IN_CREATE whereas it is useful for IN_MOVE_TO).
  383.  
  384.     The possible fields are:
  385.       - wd (int): Watch Descriptor.
  386.       - mask (int): Mask.
  387.       - maskname (str): Readable event name.
  388.       - path (str): path of the file or directory being watched.
  389.       - name (str): Basename of the file or directory against which the
  390.               event was raised, in case where the watched directory
  391.               is the parent directory. None if the event was raised
  392.               on the watched item itself. This field is always provided
  393.               even if the string is ''.
  394.       - pathname (str): absolute path of: path + name
  395.       - cookie (int): Cookie.
  396.       - dir (bool): is the event raised against directory.
  397.  
  398.     """
  399.     
  400.     def __init__(self, raw):
  401.         '''
  402.         Concretely, this is the raw event plus inferred infos.
  403.         '''
  404.         _Event.__init__(self, raw)
  405.         self.maskname = EventsCodes.maskname(self.mask)
  406.         
  407.         try:
  408.             if self.name:
  409.                 self.pathname = os.path.abspath(os.path.join(self.path, self.name))
  410.             else:
  411.                 self.pathname = os.path.abspath(self.path)
  412.         except AttributeError:
  413.             pass
  414.  
  415.  
  416.  
  417.  
  418. class ProcessEventError(PyinotifyError):
  419.     '''
  420.     ProcessEventError Exception. Raised on ProcessEvent error.
  421.     '''
  422.     
  423.     def __init__(self, err):
  424.         '''
  425.         @param err: Exception error description.
  426.         @type err: string
  427.         '''
  428.         PyinotifyError.__init__(self, err)
  429.  
  430.  
  431.  
  432. class _ProcessEvent:
  433.     '''
  434.     Abstract processing event class.
  435.     '''
  436.     
  437.     def __call__(self, event):
  438.         '''
  439.         To behave like a functor the object must be callable.
  440.         This method is a dispatch method. Lookup order:
  441.           1. process_MASKNAME method
  442.           2. process_FAMILY_NAME method
  443.           3. otherwise call process_default
  444.  
  445.         @param event: Event to be processed.
  446.         @type event: Event object
  447.         @return: By convention when used from the ProcessEvent class:
  448.                  - Returning False or None (default value) means keep on
  449.                  executing next chained functors (see chain.py example).
  450.                  - Returning True instead means do not execute next
  451.                    processing functions.
  452.         @rtype: bool
  453.         @raise ProcessEventError: Event object undispatchable,
  454.                                   unknown event.
  455.         '''
  456.         stripped_mask = event.mask - (event.mask & IN_ISDIR)
  457.         maskname = EventsCodes.ALL_VALUES.get(stripped_mask)
  458.         if maskname is None:
  459.             raise ProcessEventError('Unknown mask 0x%08x' % stripped_mask)
  460.         maskname is None
  461.         meth = getattr(self, 'process_' + maskname, None)
  462.         if meth is not None:
  463.             return meth(event)
  464.         meth = getattr(self, 'process_IN_' + maskname.split('_')[1], None)
  465.         if meth is not None:
  466.             return meth(event)
  467.         return self.process_default(event)
  468.  
  469.     
  470.     def __repr__(self):
  471.         return '<%s>' % self.__class__.__name__
  472.  
  473.  
  474.  
  475. class _SysProcessEvent(_ProcessEvent):
  476.     '''
  477.     There is three kind of processing according to each event:
  478.  
  479.       1. special handling (deletion from internal container, bug, ...).
  480.       2. default treatment: which is applied to most of events.
  481.       4. IN_ISDIR is never sent alone, he is piggybacked with a standart
  482.          event, he is not processed as the others events, instead, its
  483.          value is captured and appropriately aggregated to dst event.
  484.     '''
  485.     
  486.     def __init__(self, wm, notifier):
  487.         '''
  488.  
  489.         @param wm: Watch Manager.
  490.         @type wm: WatchManager instance
  491.         @param notifier: notifier.
  492.         @type notifier: Instance of Notifier.
  493.         '''
  494.         self._watch_manager = wm
  495.         self._notifier = notifier
  496.         self._mv_cookie = { }
  497.         self._mv = { }
  498.  
  499.     
  500.     def cleanup(self):
  501.         '''
  502.         Cleanup (delete) old (>1mn) records contained in self._mv_cookie
  503.         and self._mv.
  504.         '''
  505.         date_cur_ = datetime.now()
  506.         for seq in [
  507.             self._mv_cookie,
  508.             self._mv]:
  509.             for k in seq.keys():
  510.                 if date_cur_ - seq[k][1] > timedelta(minutes = 1):
  511.                     log.debug('cleanup: deleting entry %s' % seq[k][0])
  512.                     del seq[k]
  513.                     continue
  514.             
  515.         
  516.  
  517.     
  518.     def process_IN_CREATE(self, raw_event):
  519.         """
  520.         If the event concerns a directory and the auto_add flag of the
  521.         targetted watch is set to True, a new watch is added on this
  522.         new directory, with the same attributes's values than those of
  523.         this watch.
  524.         """
  525.         if raw_event.mask & IN_ISDIR:
  526.             watch_ = self._watch_manager._wmd.get(raw_event.wd)
  527.             if watch_.auto_add:
  528.                 addw = self._watch_manager.add_watch
  529.                 newwd = addw(os.path.join(watch_.path, raw_event.name), watch_.mask, proc_fun = watch_.proc_fun, rec = False, auto_add = watch_.auto_add)
  530.                 base = os.path.join(watch_.path, raw_event.name)
  531.                 if newwd[base] > 0:
  532.                     for name in os.listdir(base):
  533.                         inner = os.path.join(base, name)
  534.                         if os.path.isdir(inner) and self._watch_manager.get_wd(inner) is None:
  535.                             rawevent = _RawEvent(newwd[base], IN_CREATE | IN_ISDIR, 0, name)
  536.                             self._notifier._eventq.append(rawevent)
  537.                             continue
  538.                     
  539.                 
  540.             
  541.         
  542.         return self.process_default(raw_event)
  543.  
  544.     
  545.     def process_IN_MOVED_FROM(self, raw_event):
  546.         '''
  547.         Map the cookie with the source path (+ date for cleaning).
  548.         '''
  549.         watch_ = self._watch_manager._wmd.get(raw_event.wd)
  550.         path_ = watch_.path
  551.         src_path = os.path.normpath(os.path.join(path_, raw_event.name))
  552.         self._mv_cookie[raw_event.cookie] = (src_path, datetime.now())
  553.         return self.process_default(raw_event, {
  554.             'cookie': raw_event.cookie })
  555.  
  556.     
  557.     def process_IN_MOVED_TO(self, raw_event):
  558.         '''
  559.         Map the source path with the destination path (+ date for
  560.         cleaning).
  561.         '''
  562.         watch_ = self._watch_manager._wmd.get(raw_event.wd)
  563.         path_ = watch_.path
  564.         dst_path = os.path.normpath(os.path.join(path_, raw_event.name))
  565.         mv_ = self._mv_cookie.get(raw_event.cookie)
  566.         if mv_:
  567.             self._mv[mv_[0]] = (dst_path, datetime.now())
  568.         
  569.         return self.process_default(raw_event, {
  570.             'cookie': raw_event.cookie })
  571.  
  572.     
  573.     def process_IN_MOVE_SELF(self, raw_event):
  574.         """
  575.         STATUS: the following bug has been fixed in the recent kernels (fixme:
  576.         which version ?). Now it raises IN_DELETE_SELF instead.
  577.  
  578.         Old kernels are bugged, this event is raised when the watched item
  579.         was moved, so we must update its path, but under some circumstances it
  580.         can be impossible: if its parent directory and its destination
  581.         directory aren't watched. The kernel (see include/linux/fsnotify.h)
  582.         doesn't bring us enough informations like the destination path of
  583.         moved items.
  584.         """
  585.         watch_ = self._watch_manager._wmd.get(raw_event.wd)
  586.         src_path = watch_.path
  587.         mv_ = self._mv.get(src_path)
  588.         if mv_:
  589.             watch_.path = mv_[0]
  590.         else:
  591.             log.error('The path %s of this watch %s must not be trusted anymore' % (watch_.path, watch_))
  592.             if not watch_.path.endswith('-wrong-path'):
  593.                 watch_.path += '-wrong-path'
  594.             
  595.         return self.process_default(raw_event)
  596.  
  597.     
  598.     def process_IN_Q_OVERFLOW(self, raw_event):
  599.         '''
  600.         Only signal overflow, most of the common flags are irrelevant
  601.         for this event (path, wd, name).
  602.         '''
  603.         return Event({
  604.             'mask': raw_event.mask })
  605.  
  606.     
  607.     def process_IN_IGNORED(self, raw_event):
  608.         '''
  609.         The watch descriptor raised by this event is now ignored (forever),
  610.         it can be safely deleted from watch manager dictionary.
  611.         After this event we can be sure that neither the event queue
  612.         neither the system will raise an event associated to this wd.
  613.         '''
  614.         event_ = self.process_default(raw_event)
  615.         
  616.         try:
  617.             del self._watch_manager._wmd[raw_event.wd]
  618.         except KeyError:
  619.             err = None
  620.             log.error(err)
  621.  
  622.         return event_
  623.  
  624.     
  625.     def process_default(self, raw_event, to_append = { }):
  626.         '''
  627.         Common handling for the following events:
  628.  
  629.         IN_ACCESS, IN_MODIFY, IN_ATTRIB, IN_CLOSE_WRITE, IN_CLOSE_NOWRITE,
  630.         IN_OPEN, IN_DELETE, IN_DELETE_SELF, IN_UNMOUNT.
  631.         '''
  632.         ret = None
  633.         watch_ = self._watch_manager._wmd.get(raw_event.wd)
  634.         if raw_event.mask & (IN_DELETE_SELF | IN_MOVE_SELF):
  635.             dir_ = watch_.dir
  636.         else:
  637.             dir_ = bool(raw_event.mask & IN_ISDIR)
  638.         dict_ = {
  639.             'wd': raw_event.wd,
  640.             'mask': raw_event.mask,
  641.             'path': watch_.path,
  642.             'name': raw_event.name,
  643.             'dir': dir_ }
  644.         dict_.update(to_append)
  645.         return Event(dict_)
  646.  
  647.  
  648.  
  649. class ProcessEvent(_ProcessEvent):
  650.     """
  651.     Process events objects, can be specialized via subclassing, thus its
  652.     behavior can be overriden:
  653.  
  654.     Note: you should not override __init__ in your subclass instead define
  655.     a my_init() method, this method will be called from the constructor of
  656.     this class with optional parameters.
  657.  
  658.       1. Provide methods, e.g. process_IN_DELETE for processing a given kind
  659.          of event (eg. IN_DELETE in this case).
  660.       2. Or/and provide methods for processing events by 'family', e.g.
  661.          process_IN_CLOSE method will process both IN_CLOSE_WRITE and
  662.          IN_CLOSE_NOWRITE events (if process_IN_CLOSE_WRITE and
  663.          process_IN_CLOSE_NOWRITE aren't defined).
  664.       3. Or/and override process_default for processing the remaining kind of
  665.          events.
  666.     """
  667.     pevent = None
  668.     
  669.     def __init__(self, pevent = None, **kargs):
  670.         '''
  671.         Enable chaining of ProcessEvent instances.
  672.  
  673.         @param pevent: optional callable object, will be called on event
  674.                        processing (before self).
  675.         @type pevent: callable
  676.         @param kargs: optional arguments delagated to template method my_init
  677.         @type kargs: dict
  678.         '''
  679.         self.pevent = pevent
  680.         self.my_init(**kargs)
  681.  
  682.     
  683.     def my_init(self, **kargs):
  684.         """
  685.         Override this method when subclassing if you want to achieve
  686.         custom initialization of your subclass' instance. You MUST pass
  687.         keyword arguments. This method does nothing by default.
  688.  
  689.         @param kargs: optional arguments delagated to template method my_init
  690.         @type kargs: dict
  691.         """
  692.         pass
  693.  
  694.     
  695.     def __call__(self, event):
  696.         stop_chaining = False
  697.         if self.pevent is not None:
  698.             stop_chaining = self.pevent(event)
  699.         
  700.         if not stop_chaining:
  701.             return _ProcessEvent.__call__(self, event)
  702.  
  703.     
  704.     def nested_pevent(self):
  705.         return self.pevent
  706.  
  707.     
  708.     def process_default(self, event):
  709.         '''
  710.         Default default processing event method. Print event
  711.         on standart output.
  712.  
  713.         @param event: Event to be processed.
  714.         @type event: Event instance
  715.         '''
  716.         print repr(event)
  717.  
  718.  
  719.  
  720. class ChainIfTrue(ProcessEvent):
  721.     '''
  722.     Makes conditional chaining depending on the result of the nested
  723.     processing instance.
  724.     '''
  725.     
  726.     def my_init(self, func):
  727.         self._func = func
  728.  
  729.     
  730.     def process_default(self, event):
  731.         return not self._func(event)
  732.  
  733.  
  734.  
  735. class Stats(ProcessEvent):
  736.     
  737.     def my_init(self):
  738.         self._start_time = time.time()
  739.         self._stats = { }
  740.         self._stats_lock = threading.Lock()
  741.  
  742.     
  743.     def process_default(self, event):
  744.         self._stats_lock.acquire()
  745.         
  746.         try:
  747.             events = event.maskname.split('|')
  748.             for event_name in events:
  749.                 count = self._stats.get(event_name, 0)
  750.                 self._stats[event_name] = count + 1
  751.         finally:
  752.             self._stats_lock.release()
  753.  
  754.  
  755.     
  756.     def _stats_copy(self):
  757.         self._stats_lock.acquire()
  758.         
  759.         try:
  760.             return self._stats.copy()
  761.         finally:
  762.             self._stats_lock.release()
  763.  
  764.  
  765.     
  766.     def __repr__(self):
  767.         stats = self._stats_copy()
  768.         t = int(time.time() - self._start_time)
  769.         if t < 60:
  770.             ts = str(t) + 'sec'
  771.         elif t <= t:
  772.             pass
  773.         elif t < 3600:
  774.             ts = '%dmn%dsec' % (t / 60, t % 60)
  775.         elif t <= t:
  776.             pass
  777.         elif t < 86400:
  778.             ts = '%dh%dmn' % (t / 3600, (t % 3600) / 60)
  779.         elif t >= 86400:
  780.             ts = '%dd%dh' % (t / 86400, (t % 86400) / 3600)
  781.         
  782.         stats['ElapsedTime'] = ts
  783.         l = []
  784.         for ev, value in sorted(stats.items(), key = (lambda x: x[0])):
  785.             l.append(' %s=%s' % (Color.FieldName(ev), Color.FieldValue(value)))
  786.         
  787.         s = '<%s%s >' % (Color.ClassName(self.__class__.__name__), ''.join(l))
  788.         return s
  789.  
  790.     
  791.     def dump(self, filename):
  792.         fo = file(filename, 'wb')
  793.         
  794.         try:
  795.             fo.write(str(self))
  796.         finally:
  797.             fo.close()
  798.  
  799.  
  800.     
  801.     def __str__(self, scale = 45):
  802.         stats = self._stats_copy()
  803.         if not stats:
  804.             return ''
  805.         m = max(stats.values())
  806.         if not int(round(float(m) / scale)):
  807.             pass
  808.         unity = 1
  809.         fmt = '%%-26s%%-%ds%%s' % (len(Color.FieldValue('@' * scale)) + 1)
  810.         
  811.         def func(x):
  812.             return fmt % (Color.FieldName(x[0]), Color.FieldValue('@' * (x[1] / unity)), Color.Simple('%d' % x[1], 'yellow'))
  813.  
  814.         s = '\n'.join(map(func, sorted(stats.items(), key = (lambda x: x[0]))))
  815.         return s
  816.  
  817.  
  818.  
  819. class NotifierError(PyinotifyError):
  820.     '''
  821.     Notifier Exception. Raised on Notifier error.
  822.  
  823.     '''
  824.     
  825.     def __init__(self, err):
  826.         """
  827.         @param err: Exception string's description.
  828.         @type err: string
  829.         """
  830.         PyinotifyError.__init__(self, err)
  831.  
  832.  
  833.  
  834. class Notifier:
  835.     '''
  836.     Read notifications, process events.
  837.  
  838.     '''
  839.     
  840.     def __init__(self, watch_manager, default_proc_fun = ProcessEvent(), read_freq = 0, treshold = 0, timeout = None):
  841.         '''
  842.         Initialization. read_freq, treshold and timeout parameters are used
  843.         when looping.
  844.  
  845.         @param watch_manager: Watch Manager.
  846.         @type watch_manager: WatchManager instance
  847.         @param default_proc_fun: Default processing method.
  848.         @type default_proc_fun: instance of ProcessEvent
  849.         @param read_freq: if read_freq == 0, events are read asap,
  850.                           if read_freq is > 0, this thread sleeps
  851.                           max(0, read_freq - timeout) seconds. But if
  852.                           timeout is None it can be different because
  853.                           poll is blocking waiting for something to read.
  854.         @type read_freq: int
  855.         @param treshold: File descriptor will be read only if its size to
  856.                          read is >= treshold. If != 0, you likely want to
  857.                          use it in combination with read_freq because
  858.                          without that you keep looping without really reading
  859.                          anything and that until the amount to read
  860.                          is >= treshold. At least with read_freq you may sleep.
  861.         @type treshold: int
  862.         @param timeout:
  863.             http://docs.python.org/lib/poll-objects.html#poll-objects
  864.         @type timeout: int
  865.         '''
  866.         self._watch_manager = watch_manager
  867.         self._fd = self._watch_manager._fd
  868.         self._pollobj = select.poll()
  869.         self._pollobj.register(self._fd, select.POLLIN)
  870.         self._pipe = (-1, -1)
  871.         self._eventq = deque()
  872.         self._sys_proc_fun = _SysProcessEvent(self._watch_manager, self)
  873.         self._default_proc_fun = default_proc_fun
  874.         self._read_freq = read_freq
  875.         self._treshold = treshold
  876.         self._timeout = timeout
  877.  
  878.     
  879.     def proc_fun(self):
  880.         return self._default_proc_fun
  881.  
  882.     
  883.     def check_events(self):
  884.         '''
  885.         Check for new events available to read, blocks up to timeout
  886.         milliseconds.
  887.  
  888.         @return: New events to read.
  889.         @rtype: bool
  890.         '''
  891.         while True:
  892.             
  893.             try:
  894.                 ret = self._pollobj.poll(self._timeout)
  895.             except select.error:
  896.                 err = None
  897.                 if err[0] == errno.EINTR:
  898.                     continue
  899.                 else:
  900.                     raise 
  901.                 err[0] == errno.EINTR
  902.  
  903.             break
  904.         if not ret or self._pipe[0] == ret[0][0]:
  905.             return False
  906.         return ret[0][1] & select.POLLIN
  907.  
  908.     
  909.     def read_events(self):
  910.         '''
  911.         Read events from device, build _RawEvents, and enqueue them.
  912.         '''
  913.         buf_ = array.array('i', [
  914.             0])
  915.         if fcntl.ioctl(self._fd, termios.FIONREAD, buf_, 1) == -1:
  916.             return None
  917.         queue_size = buf_[0]
  918.         if queue_size < self._treshold:
  919.             log.debug('(fd: %d) %d bytes available to read but treshold is fixed to %d bytes' % (self._fd, queue_size, self._treshold))
  920.             return None
  921.         
  922.         try:
  923.             r = os.read(self._fd, queue_size)
  924.         except Exception:
  925.             queue_size < self._treshold
  926.             msg = queue_size < self._treshold
  927.             fcntl.ioctl(self._fd, termios.FIONREAD, buf_, 1) == -1
  928.             raise NotifierError(msg)
  929.         except:
  930.             queue_size < self._treshold
  931.  
  932.         log.debug('event queue size: %d' % queue_size)
  933.         rsum = 0
  934.         while rsum < queue_size:
  935.             s_size = 16
  936.             s_ = struct.unpack('iIII', r[rsum:rsum + s_size])
  937.             fname_len = s_[3]
  938.             s_ = s_[:-1]
  939.             s_ += struct.unpack('%ds' % fname_len, r[rsum + s_size:rsum + s_size + fname_len])
  940.             self._eventq.append(_RawEvent(*s_))
  941.             rsum += s_size + fname_len
  942.             continue
  943.             queue_size < self._treshold
  944.  
  945.     
  946.     def process_events(self):
  947.         '''
  948.         Routine for processing events from queue by calling their
  949.         associated proccessing function (instance of ProcessEvent).
  950.         It also do internal processings, to keep the system updated.
  951.         '''
  952.         while self._eventq:
  953.             raw_event = self._eventq.popleft()
  954.             watch_ = self._watch_manager._wmd.get(raw_event.wd)
  955.             revent = self._sys_proc_fun(raw_event)
  956.             if watch_ and watch_.proc_fun:
  957.                 watch_.proc_fun(revent)
  958.                 continue
  959.             self._default_proc_fun(revent)
  960.         self._sys_proc_fun.cleanup()
  961.  
  962.     
  963.     def __daemonize(self, pid_file = None, force_kill = False, stdin = os.devnull, stdout = os.devnull, stderr = os.devnull):
  964.         '''
  965.         pid_file: file to which pid will be written.
  966.         force_kill: if True kill the process associated to pid_file.
  967.         stdin, stdout, stderr: files associated to common streams.
  968.         '''
  969.         if pid_file is None:
  970.             dirname = '/var/run/'
  971.             if not sys.argv[0]:
  972.                 pass
  973.             basename = 'pyinotify'
  974.             pid_file = os.path.join(dirname, basename + '.pid')
  975.         
  976.         
  977.         def fork_daemon():
  978.             pid = os.fork()
  979.             if pid == 0:
  980.                 os.setsid()
  981.                 pid = os.fork()
  982.                 if pid == 0:
  983.                     os.chdir('/')
  984.                     os.umask(0)
  985.                 else:
  986.                     os._exit(0)
  987.             else:
  988.                 os._exit(0)
  989.             fd_inp = os.open(stdin, os.O_RDONLY)
  990.             os.dup2(fd_inp, 0)
  991.             fd_out = os.open(stdout, os.O_WRONLY | os.O_CREAT)
  992.             os.dup2(fd_out, 1)
  993.             fd_err = os.open(stderr, os.O_WRONLY | os.O_CREAT)
  994.             os.dup2(fd_err, 2)
  995.  
  996.         fork_daemon()
  997.         fo = file(pid_file, 'wb')
  998.         
  999.         try:
  1000.             fo.write(str(os.getpid()) + '\n')
  1001.         finally:
  1002.             fo.close()
  1003.  
  1004.         (atexit.register,)((lambda : os.unlink(pid_file)))
  1005.  
  1006.     
  1007.     def _sleep(self, ref_time):
  1008.         if self._read_freq > 0:
  1009.             cur_time = time.time()
  1010.             sleep_amount = self._read_freq - cur_time - ref_time
  1011.             if sleep_amount > 0:
  1012.                 log.debug('Now sleeping %d seconds' % sleep_amount)
  1013.                 time.sleep(sleep_amount)
  1014.             
  1015.         
  1016.  
  1017.     
  1018.     def loop(self, callback = None, daemonize = False, **args):
  1019.         '''
  1020.         Events are read only once time every min(read_freq, timeout)
  1021.         seconds at best and only if the size to read is >= treshold.
  1022.  
  1023.         @param callback: Functor called after each event processing. Expects
  1024.                          to receive notifier object (self) as first parameter.
  1025.         @type callback: callable
  1026.         @param daemonize: This thread is daemonized if set to True.
  1027.         @type daemonize: boolean
  1028.         '''
  1029.         if daemonize:
  1030.             self._Notifier__daemonize(**args)
  1031.         
  1032.         while None:
  1033.             
  1034.             try:
  1035.                 self.process_events()
  1036.                 if callback is not None:
  1037.                     callback(self)
  1038.                 
  1039.                 ref_time = time.time()
  1040.                 if self.check_events():
  1041.                     self._sleep(ref_time)
  1042.                     self.read_events()
  1043.             continue
  1044.             except KeyboardInterrupt:
  1045.                 log.debug('Pyinotify stops monitoring.')
  1046.                 self.stop()
  1047.                 break
  1048.                 continue
  1049.             
  1050.  
  1051.             return None
  1052.  
  1053.     
  1054.     def stop(self):
  1055.         """
  1056.         Close the inotify's instance (close its file descriptor).
  1057.         It destroys all existing watches, pending events,...
  1058.         """
  1059.         self._pollobj.unregister(self._fd)
  1060.         os.close(self._fd)
  1061.  
  1062.  
  1063.  
  1064. class ThreadedNotifier(threading.Thread, Notifier):
  1065.     '''
  1066.     This notifier inherits from threading.Thread for instantiating a separate
  1067.     thread, and also inherits from Notifier, because it is a threaded notifier.
  1068.  
  1069.     Note that everything possible with this class is also possible through
  1070.     Notifier. Moreover Notifier is _better_ under many aspects: not threaded,
  1071.     can be easily daemonized.
  1072.     '''
  1073.     
  1074.     def __init__(self, watch_manager, default_proc_fun = ProcessEvent(), read_freq = 0, treshold = 0, timeout = None):
  1075.         '''
  1076.         Initialization, initialize base classes. read_freq, treshold and
  1077.         timeout parameters are used when looping.
  1078.  
  1079.         @param watch_manager: Watch Manager.
  1080.         @type watch_manager: WatchManager instance
  1081.         @param default_proc_fun: Default processing method.
  1082.         @type default_proc_fun: instance of ProcessEvent
  1083.         @param read_freq: if read_freq == 0, events are read asap,
  1084.                           if read_freq is > 0, this thread sleeps
  1085.                           max(0, read_freq - timeout) seconds.
  1086.         @type read_freq: int
  1087.         @param treshold: File descriptor will be read only if its size to
  1088.                          read is >= treshold. If != 0, you likely want to
  1089.                          use it in combination with read_freq because
  1090.                          without that you keep looping without really reading
  1091.                          anything and that until the amount to read
  1092.                          is >= treshold. At least with read_freq you may sleep.
  1093.         @type treshold: int
  1094.         @param timeout:
  1095.            see http://docs.python.org/lib/poll-objects.html#poll-objects
  1096.            Read the corresponding comment in the source code before changing
  1097.            it.
  1098.         @type timeout: int
  1099.         '''
  1100.         threading.Thread.__init__(self)
  1101.         self._stop_event = threading.Event()
  1102.         Notifier.__init__(self, watch_manager, default_proc_fun, read_freq, treshold, timeout)
  1103.         self._pipe = os.pipe()
  1104.         self._pollobj.register(self._pipe[0], select.POLLIN)
  1105.  
  1106.     
  1107.     def stop(self):
  1108.         """
  1109.         Stop the notifier's loop. Stop notification. Join the thread.
  1110.         """
  1111.         self._stop_event.set()
  1112.         os.write(self._pipe[1], 'stop')
  1113.         threading.Thread.join(self)
  1114.         Notifier.stop(self)
  1115.         self._pollobj.unregister(self._pipe[0])
  1116.         os.close(self._pipe[0])
  1117.         os.close(self._pipe[1])
  1118.  
  1119.     
  1120.     def loop(self):
  1121.         """
  1122.         Thread's main loop. Don't meant to be called by user directly.
  1123.         Call start() instead.
  1124.  
  1125.         Events are read only once time every min(read_freq, timeout)
  1126.         seconds at best and only if the size of events to read is >= treshold.
  1127.         """
  1128.         while not self._stop_event.isSet():
  1129.             self.process_events()
  1130.             ref_time = time.time()
  1131.             if self.check_events():
  1132.                 self._sleep(ref_time)
  1133.                 self.read_events()
  1134.                 continue
  1135.  
  1136.     
  1137.     def run(self):
  1138.         """
  1139.         Start the thread's loop: read and process events until the method
  1140.         stop() is called.
  1141.         Never call this method directly, instead call the start() method
  1142.         inherited from threading.Thread, which then will call run().
  1143.         """
  1144.         self.loop()
  1145.  
  1146.  
  1147.  
  1148. class Watch:
  1149.     '''
  1150.     Represent a watch, i.e. a file or directory being watched.
  1151.  
  1152.     '''
  1153.     
  1154.     def __init__(self, **keys):
  1155.         '''
  1156.         Initializations.
  1157.  
  1158.         @param wd: Watch descriptor.
  1159.         @type wd: int
  1160.         @param path: Path of the file or directory being watched.
  1161.         @type path: str
  1162.         @param mask: Mask.
  1163.         @type mask: int
  1164.         @param proc_fun: Processing callable object.
  1165.         @type proc_fun:
  1166.         @param auto_add: Automatically add watches on new directories.
  1167.         @type auto_add: bool
  1168.         '''
  1169.         for k, v in keys.iteritems():
  1170.             setattr(self, k, v)
  1171.         
  1172.         self.dir = os.path.isdir(self.path)
  1173.  
  1174.     
  1175.     def __repr__(self):
  1176.         '''
  1177.         @return: String representation.
  1178.         @rtype: str
  1179.         '''
  1180.         s = [](_[1])
  1181.         s = '%s%s %s %s' % (Color.Punctuation('<'), Color.ClassName(self.__class__.__name__), s, Color.Punctuation('>'))
  1182.         return s
  1183.  
  1184.  
  1185.  
  1186. class ExcludeFilter:
  1187.     '''
  1188.     ExcludeFilter is an exclusion filter.
  1189.     '''
  1190.     
  1191.     def __init__(self, arg_lst):
  1192.         """
  1193.         @param arg_lst: is either a list or dict of patterns:
  1194.                         [pattern1, ..., patternn]
  1195.                         {'filename1': (list1, listn), ...} where list1 is
  1196.                         a list of patterns
  1197.         @type arg_lst: list or dict
  1198.         """
  1199.         if isinstance(arg_lst, dict):
  1200.             lst = self._load_patterns(arg_lst)
  1201.         elif isinstance(arg_lst, list):
  1202.             lst = arg_lst
  1203.         else:
  1204.             raise TypeError
  1205.         self._lregex = isinstance(arg_lst, dict)
  1206.         for regex in lst:
  1207.             self._lregex.append(re.compile(regex, re.UNICODE))
  1208.         
  1209.  
  1210.     
  1211.     def _load_patterns(self, dct):
  1212.         lst = []
  1213.         for path, varnames in dct.iteritems():
  1214.             loc = { }
  1215.             execfile(path, { }, loc)
  1216.             for varname in varnames:
  1217.                 lst.extend(loc.get(varname, []))
  1218.             
  1219.         
  1220.         return lst
  1221.  
  1222.     
  1223.     def _match(self, regex, path):
  1224.         return regex.match(path) is not None
  1225.  
  1226.     
  1227.     def __call__(self, path):
  1228.         '''
  1229.         @param path: path to match against regexps.
  1230.         @type path: str
  1231.         @return: return True is path has been matched and should
  1232.                  be excluded, False otherwise.
  1233.         @rtype: bool
  1234.         '''
  1235.         for regex in self._lregex:
  1236.             if self._match(regex, path):
  1237.                 return True
  1238.         
  1239.         return False
  1240.  
  1241.  
  1242.  
  1243. class WatchManagerError(Exception):
  1244.     '''
  1245.     WatchManager Exception. Raised on error encountered on watches
  1246.     operations.
  1247.  
  1248.     '''
  1249.     
  1250.     def __init__(self, msg, wmd):
  1251.         """
  1252.         @param msg: Exception string's description.
  1253.         @type msg: string
  1254.         @param wmd: Results of previous operations made by the same function
  1255.                     on previous wd or paths. It also contains the item which
  1256.                     raised this exception.
  1257.         @type wmd: dict
  1258.         """
  1259.         self.wmd = wmd
  1260.         Exception.__init__(self, msg)
  1261.  
  1262.  
  1263.  
  1264. class WatchManager:
  1265.     '''
  1266.     Provide operations for watching files and directories. Integrated
  1267.     dictionary is used to reference watched items.
  1268.     '''
  1269.     
  1270.     def __init__(self, exclude_filter = (lambda path: False)):
  1271.         '''
  1272.         Initialization: init inotify, init watch manager dictionary.
  1273.         Raise OSError if initialization fails.
  1274.  
  1275.         @param exclude_filter: boolean function, returns True if current
  1276.                                path must be excluded from being watched.
  1277.                                Convenient for providing a common exclusion
  1278.                                filter for every call to add_watch.
  1279.         @type exclude_filter: bool
  1280.         '''
  1281.         self._exclude_filter = exclude_filter
  1282.         self._wmd = { }
  1283.         self._fd = LIBC.inotify_init()
  1284.         if self._fd < 0:
  1285.             raise OSError()
  1286.         self._fd < 0
  1287.  
  1288.     
  1289.     def __add_watch(self, path, mask, proc_fun, auto_add):
  1290.         '''
  1291.         Add a watch on path, build a Watch object and insert it in the
  1292.         watch manager dictionary. Return the wd value.
  1293.         '''
  1294.         wd_ = LIBC.inotify_add_watch(self._fd, ctypes.create_string_buffer(path), mask)
  1295.         if wd_ < 0:
  1296.             return wd_
  1297.         watch_ = Watch(wd = wd_, path = os.path.normpath(path), mask = mask, proc_fun = proc_fun, auto_add = auto_add)
  1298.         self._wmd[wd_] = watch_
  1299.         log.debug('New %s' % watch_)
  1300.         return wd_
  1301.  
  1302.     
  1303.     def __glob(self, path, do_glob):
  1304.         if do_glob:
  1305.             return iglob(path)
  1306.         return [
  1307.             path]
  1308.  
  1309.     
  1310.     def add_watch(self, path, mask, proc_fun = None, rec = False, auto_add = False, do_glob = False, quiet = True, exclude_filter = None):
  1311.         """
  1312.         Add watch(s) on given path(s) with the specified mask and
  1313.         optionnally with a processing function and recursive flag.
  1314.  
  1315.         @param path: Path to watch, the path can either be a file or a
  1316.                      directory. Also accepts a sequence (list) of paths.
  1317.         @type path: string or list of string
  1318.         @param mask: Bitmask of events.
  1319.         @type mask: int
  1320.         @param proc_fun: Processing object.
  1321.         @type proc_fun: function or ProcessEvent instance or instance of
  1322.                         one of its subclasses or callable object.
  1323.         @param rec: Recursively add watches from path on all its
  1324.                     subdirectories, set to False by default (doesn't
  1325.                     follows symlinks).
  1326.         @type rec: bool
  1327.         @param auto_add: Automatically add watches on newly created
  1328.                          directories in the watch's path.
  1329.         @type auto_add: bool
  1330.         @param do_glob: Do globbing on pathname.
  1331.         @type do_glob: bool
  1332.         @param quiet: if True raise an WatchManagerError exception on
  1333.                       error. See example not_quiet.py
  1334.         @type quiet: bool
  1335.         @param exclude_filter: boolean function, returns True if current
  1336.                                path must be excluded from being watched.
  1337.                                Has precedence on exclude_filter defined
  1338.                                into __init__.
  1339.         @type exclude_filter: bool
  1340.         @return: dict of paths associated to watch descriptors. A wd value
  1341.                  is positive if the watch has been sucessfully added,
  1342.                  otherwise the value is negative. If the path is invalid
  1343.                  it will be not included into this dict.
  1344.         @rtype: dict of {str: int}
  1345.         """
  1346.         ret_ = { }
  1347.         if exclude_filter is None:
  1348.             exclude_filter = self._exclude_filter
  1349.         
  1350.         for npath in self._WatchManager__format_param(path):
  1351.             for apath in self._WatchManager__glob(npath, do_glob):
  1352.                 for rpath in self._WatchManager__walk_rec(apath, rec):
  1353.                     if not exclude_filter(rpath):
  1354.                         wd = ret_[rpath] = self._WatchManager__add_watch(rpath, mask, proc_fun, auto_add)
  1355.                         if wd < 0:
  1356.                             err = 'add_watch: cannot watch %s (WD=%d)'
  1357.                             err = err % (rpath, wd)
  1358.                             if quiet:
  1359.                                 log.error(err)
  1360.                             else:
  1361.                                 raise WatchManagerError(err, ret_)
  1362.                         quiet
  1363.                         continue
  1364.                     ret_[rpath] = -2
  1365.                 
  1366.             
  1367.         
  1368.         return ret_
  1369.  
  1370.     
  1371.     def __get_sub_rec(self, lpath):
  1372.         """
  1373.         Get every wd from self._wmd if its path is under the path of
  1374.         one (at least) of those in lpath. Doesn't follow symlinks.
  1375.  
  1376.         @param lpath: list of watch descriptor
  1377.         @type lpath: list of int
  1378.         @return: list of watch descriptor
  1379.         @rtype: list of int
  1380.         """
  1381.         for d in lpath:
  1382.             root = self.get_path(d)
  1383.             if root:
  1384.                 yield d
  1385.             
  1386.             if not os.path.isdir(root):
  1387.                 continue
  1388.             
  1389.             root = os.path.normpath(root)
  1390.             lend = len(root)
  1391.             for iwd in self._wmd.items():
  1392.                 cur = iwd[1].path
  1393.                 pref = os.path.commonprefix([
  1394.                     root,
  1395.                     cur])
  1396.                 if (root == os.sep or len(pref) == lend) and len(cur) > lend and cur[lend] == os.sep:
  1397.                     yield iwd[1].wd
  1398.                     continue
  1399.             
  1400.         
  1401.  
  1402.     
  1403.     def update_watch(self, wd, mask = None, proc_fun = None, rec = False, auto_add = False, quiet = True):
  1404.         """
  1405.         Update existing watch(s). Both the mask and the processing
  1406.         object can be modified.
  1407.  
  1408.         @param wd: Watch Descriptor to update. Also accepts a list of
  1409.                      watch descriptors.
  1410.         @type wd: int or list of int
  1411.         @param mask: Optional new bitmask of events.
  1412.         @type mask: int
  1413.         @param proc_fun: Optional new processing function.
  1414.         @type proc_fun: function or ProcessEvent instance or instance of
  1415.                         one of its subclasses or callable object.
  1416.         @param rec: Recursively update watches on every already watched
  1417.                     subdirectories and subfiles.
  1418.         @type rec: bool
  1419.         @param auto_add: Automatically add watches on newly created
  1420.                          directories in the watch's path.
  1421.         @type auto_add: bool
  1422.         @param quiet: if True raise an WatchManagerError exception on
  1423.                       error. See example not_quiet.py
  1424.         @type quiet: bool
  1425.         @return: dict of watch descriptors associated to booleans values.
  1426.                  True if the corresponding wd has been successfully
  1427.                  updated, False otherwise.
  1428.         @rtype: dict of int: bool
  1429.         """
  1430.         lwd = self._WatchManager__format_param(wd)
  1431.         if rec:
  1432.             lwd = self._WatchManager__get_sub_rec(lwd)
  1433.         
  1434.         ret_ = { }
  1435.         for awd in lwd:
  1436.             apath = self.get_path(awd)
  1437.             if not apath or awd < 0:
  1438.                 err = 'update_watch: invalid WD=%d' % awd
  1439.                 if quiet:
  1440.                     log.error(err)
  1441.                     continue
  1442.                 
  1443.                 raise WatchManagerError(err, ret_)
  1444.             awd < 0
  1445.             if mask:
  1446.                 addw = LIBC.inotify_add_watch
  1447.                 wd_ = addw(self._fd, ctypes.create_string_buffer(apath), mask)
  1448.                 if wd_ < 0:
  1449.                     ret_[awd] = False
  1450.                     err = 'update_watch: cannot update WD=%d (%s)' % (wd_, apath)
  1451.                     if quiet:
  1452.                         log.error(err)
  1453.                         continue
  1454.                     
  1455.                     raise WatchManagerError(err, ret_)
  1456.                 wd_ < 0
  1457.                 if not awd == wd_:
  1458.                     raise AssertionError
  1459.             
  1460.             if proc_fun or auto_add:
  1461.                 watch_ = self._wmd[awd]
  1462.             
  1463.             if proc_fun:
  1464.                 watch_.proc_fun = proc_fun
  1465.             
  1466.             if auto_add:
  1467.                 watch_.proc_fun = auto_add
  1468.             
  1469.             ret_[awd] = True
  1470.             log.debug('Updated watch - %s' % self._wmd[awd])
  1471.         
  1472.         return ret_
  1473.  
  1474.     
  1475.     def __format_param(self, param):
  1476.         '''
  1477.         @param param: Parameter.
  1478.         @type param: string or int
  1479.         @return: wrap param.
  1480.         @rtype: list of type(param)
  1481.         '''
  1482.         if isinstance(param, list):
  1483.             for p_ in param:
  1484.                 yield p_
  1485.             
  1486.         else:
  1487.             yield param
  1488.  
  1489.     
  1490.     def get_wd(self, path):
  1491.         '''
  1492.         Returns the watch descriptor associated to path. This method
  1493.         has an prohibitive cost, always prefer to keep the WD.
  1494.         If path is unknown None is returned.
  1495.  
  1496.         @param path: path.
  1497.         @type path: str
  1498.         @return: WD or None.
  1499.         @rtype: int or None
  1500.         '''
  1501.         path = os.path.normpath(path)
  1502.         for iwd in self._wmd.iteritems():
  1503.             if iwd[1].path == path:
  1504.                 return iwd[0]
  1505.         
  1506.         log.debug('get_wd: unknown path %s' % path)
  1507.  
  1508.     
  1509.     def get_path(self, wd):
  1510.         '''
  1511.         Returns the path associated to WD, if WD is unknown
  1512.         None is returned.
  1513.  
  1514.         @param wd: watch descriptor.
  1515.         @type wd: int
  1516.         @return: path or None.
  1517.         @rtype: string or None
  1518.         '''
  1519.         watch_ = self._wmd.get(wd)
  1520.         if watch_:
  1521.             return watch_.path
  1522.         log.debug('get_path: unknown WD %d' % wd)
  1523.  
  1524.     
  1525.     def __walk_rec(self, top, rec):
  1526.         """
  1527.         Yields each subdirectories of top, doesn't follow symlinks.
  1528.         If rec is false, only yield top.
  1529.  
  1530.         @param top: root directory.
  1531.         @type top: string
  1532.         @param rec: recursive flag.
  1533.         @type rec: bool
  1534.         @return: path of one subdirectory.
  1535.         @rtype: string
  1536.         """
  1537.         if not rec and os.path.islink(top) or not os.path.isdir(top):
  1538.             yield top
  1539.         else:
  1540.             for root, dirs, files in os.walk(top):
  1541.                 yield root
  1542.             
  1543.  
  1544.     
  1545.     def rm_watch(self, wd, rec = False, quiet = True):
  1546.         '''
  1547.         Removes watch(s).
  1548.  
  1549.         @param wd: Watch Descriptor of the file or directory to unwatch.
  1550.                    Also accepts a list of WDs.
  1551.         @type wd: int or list of int.
  1552.         @param rec: Recursively removes watches on every already watched
  1553.                     subdirectories and subfiles.
  1554.         @type rec: bool
  1555.         @param quiet: if True raise an WatchManagerError exception on
  1556.                       error. See example not_quiet.py
  1557.         @type quiet: bool
  1558.         @return: dict of watch descriptors associated to booleans values.
  1559.                  True if the corresponding wd has been successfully
  1560.                  removed, False otherwise.
  1561.         @rtype: dict of int: bool
  1562.         '''
  1563.         lwd = self._WatchManager__format_param(wd)
  1564.         if rec:
  1565.             lwd = self._WatchManager__get_sub_rec(lwd)
  1566.         
  1567.         ret_ = { }
  1568.         for awd in lwd:
  1569.             wd_ = LIBC.inotify_rm_watch(self._fd, awd)
  1570.             if wd_ < 0:
  1571.                 ret_[awd] = False
  1572.                 err = 'rm_watch: cannot remove WD=%d' % awd
  1573.                 if quiet:
  1574.                     log.error(err)
  1575.                     continue
  1576.                 
  1577.                 raise WatchManagerError(err, ret_)
  1578.             wd_ < 0
  1579.             ret_[awd] = True
  1580.             log.debug('watch WD=%d (%s) removed' % (awd, self.get_path(awd)))
  1581.         
  1582.         return ret_
  1583.  
  1584.     
  1585.     def watch_transient_file(self, filename, mask, proc_class):
  1586.         """
  1587.         Watch a transient file, which will be created and deleted frequently
  1588.         over time (e.g. pid file).
  1589.  
  1590.         @attention: Under the call to this function it will be impossible
  1591.         to correctly watch the events triggered into the same
  1592.         base directory than the directory where is located this watched
  1593.         transient file. For instance it would actually be wrong to make these
  1594.         two successive calls: wm.watch_transient_file('/var/run/foo.pid', ...)
  1595.         and wm.add_watch('/var/run/', ...)
  1596.  
  1597.         @param filename: Filename.
  1598.         @type filename: string
  1599.         @param mask: Bitmask of events, should contain IN_CREATE and IN_DELETE.
  1600.         @type mask: int
  1601.         @param proc_class: ProcessEvent (or of one of its subclass), beware of
  1602.                            accepting a ProcessEvent's instance as argument into
  1603.                            __init__, see transient_file.py example for more
  1604.                            details.
  1605.         @type proc_class: ProcessEvent's instance or of one of its subclasses.
  1606.         @return: See add_watch().
  1607.         @rtype: See add_watch().
  1608.         """
  1609.         dirname = os.path.dirname(filename)
  1610.         if dirname == '':
  1611.             return { }
  1612.         basename = os.path.basename(filename)
  1613.         mask |= IN_CREATE | IN_DELETE
  1614.         
  1615.         def cmp_name(event):
  1616.             return basename == event.name
  1617.  
  1618.         return self.add_watch(dirname, mask, proc_fun = proc_class(ChainIfTrue(func = cmp_name)), rec = False, auto_add = False, do_glob = False)
  1619.  
  1620.  
  1621.  
  1622. class Color:
  1623.     normal = '\x1b[0m'
  1624.     black = '\x1b[30m'
  1625.     red = '\x1b[31m'
  1626.     green = '\x1b[32m'
  1627.     yellow = '\x1b[33m'
  1628.     blue = '\x1b[34m'
  1629.     purple = '\x1b[35m'
  1630.     cyan = '\x1b[36m'
  1631.     bold = '\x1b[1m'
  1632.     uline = '\x1b[4m'
  1633.     blink = '\x1b[5m'
  1634.     invert = '\x1b[7m'
  1635.     
  1636.     def Punctuation(s):
  1637.         return Color.normal + s + Color.normal
  1638.  
  1639.     Punctuation = staticmethod(Punctuation)
  1640.     
  1641.     def FieldValue(s):
  1642.         if not isinstance(s, str):
  1643.             s = str(s)
  1644.         
  1645.         return Color.purple + s + Color.normal
  1646.  
  1647.     FieldValue = staticmethod(FieldValue)
  1648.     
  1649.     def FieldName(s):
  1650.         return Color.blue + s + Color.normal
  1651.  
  1652.     FieldName = staticmethod(FieldName)
  1653.     
  1654.     def ClassName(s):
  1655.         return Color.red + Color.bold + s + Color.normal
  1656.  
  1657.     ClassName = staticmethod(ClassName)
  1658.     
  1659.     def Simple(s, color):
  1660.         if not isinstance(s, str):
  1661.             s = str(s)
  1662.         
  1663.         
  1664.         try:
  1665.             color_attr = getattr(Color, color)
  1666.         except AttributeError:
  1667.             return s
  1668.  
  1669.         return color_attr + s + Color.normal
  1670.  
  1671.     Simple = staticmethod(Simple)
  1672.  
  1673.  
  1674. def command_line():
  1675.     OptionParser = OptionParser
  1676.     import optparse
  1677.     usage = 'usage: %prog [options] [path1] [path2] [pathn]'
  1678.     parser = OptionParser(usage = usage)
  1679.     parser.add_option('-v', '--verbose', action = 'store_true', dest = 'verbose', help = 'Verbose mode')
  1680.     parser.add_option('-r', '--recursive', action = 'store_true', dest = 'recursive', help = 'Add watches recursively on paths')
  1681.     parser.add_option('-a', '--auto_add', action = 'store_true', dest = 'auto_add', help = 'Automatically add watches on new directories')
  1682.     parser.add_option('-e', '--events-list', metavar = 'EVENT[,...]', dest = 'events_list', help = 'A comma-separated list of events to watch for - see the documentation for valid options (defaults to everything)')
  1683.     parser.add_option('-s', '--stats', action = 'store_true', dest = 'stats', help = 'Display statistics')
  1684.     (options, args) = parser.parse_args()
  1685.     if options.verbose:
  1686.         log.setLevel(10)
  1687.     
  1688.     if len(args) < 1:
  1689.         path = '/tmp'
  1690.     else:
  1691.         path = args
  1692.     wm = WatchManager()
  1693.     if options.stats:
  1694.         notifier = Notifier(wm, default_proc_fun = Stats(), read_freq = 5)
  1695.     else:
  1696.         notifier = Notifier(wm)
  1697.     mask = 0
  1698.     if options.events_list:
  1699.         events_list = options.events_list.split(',')
  1700.         for ev in events_list:
  1701.             evcode = EventsCodes.ALL_FLAGS.get(ev, 0)
  1702.             if evcode:
  1703.                 mask |= evcode
  1704.                 continue
  1705.             parser.error("The event '%s' specified with option -e is not valid" % ev)
  1706.         
  1707.     else:
  1708.         mask = ALL_EVENTS
  1709.     cb_fun = None
  1710.     if options.stats:
  1711.         
  1712.         def cb(s):
  1713.             print '%s\n%s\n' % (repr(s.proc_fun()), s.proc_fun())
  1714.  
  1715.         cb_fun = cb
  1716.     
  1717.     log.debug('Start monitoring %s, (press c^c to halt pyinotify)' % path)
  1718.     wm.add_watch(path, mask, rec = options.recursive, auto_add = options.auto_add)
  1719.     notifier.loop(callback = cb_fun)
  1720.  
  1721. if __name__ == '__main__':
  1722.     command_line()
  1723.  
  1724.